From be19c888619f5113cc4b12c01df2e51ccf4375fd Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 11 Dec 2015 18:43:05 +0100 Subject: [PATCH] repo: Add _ostree_repo_allocate_tmpdir helper This creates a subdirectory of the tmp dir with a selected prefix, and takes a lockfile to ensure that nobody else is using the same directory. However, if a directory with the same prefix already exists and is not locked that is used instead. The later is useful if you want to support some kind of resumed operation on the tmpdir. touch reused dirs https://bugzilla.gnome.org/show_bug.cgi?id=757611 --- src/libostree/ostree-repo-private.h | 10 +++ src/libostree/ostree-repo.c | 131 ++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index ccb648f8..8f57f95b 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -90,6 +90,16 @@ struct OstreeRepo { OstreeRepo *parent_repo; }; +gboolean +_ostree_repo_allocate_tmpdir (int tmpdir_dfd, + const char *tmpdir_prefix, + char **tmpdir_name_out, + int *tmpdir_fd_out, + GLnxLockFile *file_lock_out, + gboolean * reusing_dir_out, + GCancellable *cancellable, + GError **error); + gboolean _ostree_repo_ensure_loose_objdir_at (int dfd, const char *loose_path, diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 1f1cba29..bc03719a 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -4617,3 +4617,134 @@ ostree_repo_regenerate_summary (OstreeRepo *self, g_list_free (ordered_keys); return ret; } + + +/* This allocates and locks a subdir of the repo tmp dir, using an existing + * one with the same prefix if it is not in use already. */ +gboolean +_ostree_repo_allocate_tmpdir (int tmpdir_dfd, + const char *tmpdir_prefix, + char **tmpdir_name_out, + int *tmpdir_fd_out, + GLnxLockFile *file_lock_out, + gboolean *reusing_dir_out, + GCancellable *cancellable, + GError **error) +{ + gboolean reusing_dir = FALSE; + g_autofree char *tmpdir_name = NULL; + glnx_fd_close int tmpdir_fd = -1; + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + /* Look for existing tmpdir (with same prefix) to reuse */ + if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error)) + return FALSE; + + while (TRUE) + { + gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, }; + struct dirent *dent; + glnx_fd_close int existing_tmpdir_fd = -1; + g_autoptr(GError) local_error = NULL; + g_autofree char *lock_name = NULL; + + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error)) + return FALSE; + + if (dent == NULL) + break; + + if (!g_str_has_prefix (dent->d_name, tmpdir_prefix)) + continue; + + /* Quickly skip non-dirs, if unknown we ignore ENOTDIR when opening instead */ + if (dent->d_type != DT_UNKNOWN && + dent->d_type != DT_DIR) + continue; + + if (!glnx_opendirat (dfd_iter.fd, dent->d_name, FALSE, + &existing_tmpdir_fd, &local_error)) + { + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) + continue; + else + { + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + } + + lock_name = g_strconcat (dent->d_name, "-lock", NULL); + + /* We put the lock outside the dir, so we can hold the lock + * until the directory is fully removed */ + if (!glnx_make_lock_file (dfd_iter.fd, lock_name, LOCK_EX | LOCK_NB, + file_lock_out, &local_error)) + { + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + continue; + else + { + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + } + + /* Touch the reused directory so that we don't accidentally + * remove it due to being old when cleaning up the tmpdir + */ + (void)futimens (existing_tmpdir_fd, NULL); + + /* We found an existing tmpdir which we managed to lock */ + tmpdir_name = g_strdup (dent->d_name); + tmpdir_fd = glnx_steal_fd (&existing_tmpdir_fd); + reusing_dir = TRUE; + } + + while (tmpdir_name == NULL) + { + g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL); + glnx_fd_close int new_tmpdir_fd = -1; + g_autoptr(GError) local_error = NULL; + g_autofree char *lock_name = NULL; + + /* No existing tmpdir found, create a new */ + + if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error)) + return FALSE; + + if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE, + &new_tmpdir_fd, error)) + return FALSE; + + lock_name = g_strconcat (tmpdir_name_template, "-lock", NULL); + + /* Note, at this point we can race with another process that picks up this + * new directory. If that happens we need to retry, making a new directory. */ + if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB, + file_lock_out, &local_error)) + { + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + continue; + else + { + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + } + + tmpdir_name = g_steal_pointer (&tmpdir_name_template); + tmpdir_fd = glnx_steal_fd (&new_tmpdir_fd); + } + + if (tmpdir_name_out) + *tmpdir_name_out = g_steal_pointer (&tmpdir_name); + + if (tmpdir_fd_out) + *tmpdir_fd_out = glnx_steal_fd (&tmpdir_fd); + + if (reusing_dir_out) + *reusing_dir_out = reusing_dir; + + return TRUE; +} -- 2.30.2